管道危害
AP SHANTHI博士
本模块的目标是讨论与流水线相关的各种危害。
我们在前面的模块中讨论了流水线和 MIPS 流水线实现的基础知识。我们对流水线进行了以下观察:
流水线不会帮助单个任务的延迟,它有助于整个工作负载的吞吐量
流水线速率受最慢流水线阶段的限制 o 多个任务同时运行
潜在加速比 = 管道级数
管道阶段的不平衡长度降低了加速
“填充”管道的时间和“排空”的时间会降低加速 o 管道级的不平衡长度降低了加速
执行数十亿条指令,因此吞吐量很重要 o 数据路径设计——理想情况下,我们期望 CPI 值为 1
流水线指令集需要什么?
可变长度指令与所有指令长度相同?
内存操作数是任何操作的一部分还是仅在加载或存储中的内存操作数?
寄存器操作数在指令格式中的多个位置与位于同一位置的寄存器?
理想情况下,我们期望 CPI 值为 1,加速比等于流水线中的阶段数。但是,有许多因素限制了这一点。管道中出现的问题称为危害。流水线中出现的危险会阻止下一条指令在其指定的时钟周期内执行。存在三种类型的危害:
结构性危害:硬件不能支持某些指令组合(流水线中的两条指令需要相同的资源)。
数据危险:指令取决于仍在流水线中的先前指令的结果
控制风险:由获取指令和关于控制流(分支和跳转)变化的决策之间的延迟引起。
结构性风险的出现是因为没有足够的资源重复。
解决结构性危害:
解决方案 1:等待
o 必须检测危险
o 必须有停止机制
o 低成本和简单
o 提高 CPI
o 用于罕见情况
解决方案 2:在问题上投入更多硬件
o 流水线硬件资源
§ 对多周期资源有用
§ 很棒的表演
§ 有时复杂,例如,RAM
o 复制资源
§ 很棒的表演
§ 增加成本(+ 可能互连延迟)
§ 对廉价或可分割的资源有用
图 11.1 显示了 MIPS 管道中结构性危害的一种可能性。指令 3 正在访问内存以获取指令,而指令 1 正在访问内存以进行数据访问(加载/存储)。这两个是相互冲突的要求,会引起危险。我们应该停止如图 11.2 所示的操作之一,或者有两个单独的代码和数据存储器。结构性危害必须在设计时自行处理。
接下来,我们将讨论数据依赖性和相关的危害。有两种类型的数据依赖——真实数据依赖和名称依赖。
•如果以下任一条件成立,则指令j是依赖于指令i 的数据:
– 指令i产生可由指令j使用的结果,或
– 指令j是依赖于指令k的数据,而指令k是依赖于指令i的数据
•当两条指令使用相同的寄存器或内存位置(称为名称),但与该名称关联的指令之间没有数据流时,就会发生名称依赖性
–在程序顺序中位于指令j之前的指令i之间的两种类型的名称依赖关系:
–当指令j写入指令i读取的寄存器或内存位置时,就会发生指令 i 和指令 j 之间的反依赖。必须保留原始顺序。
–当指令i和指令j写入相同的寄存器或内存位置时,会发生输出依赖性。必须保留指令之间的顺序。
– 由于这不是真正的依赖性,因此可以更轻松地对寄存器操作数进行重命名,这称为寄存器重命名
– 寄存器重命名可以由编译器静态完成,也可以由硬件动态完成
最后,我们讨论控制依赖。控制相关性确定指令相对于分支指令的顺序,以便以正确的程序顺序执行指令i 。控制相关性强加了两个一般约束:
– 控制依赖于其分支的指令不能移动到分支之前,因此它的执行不再受分支控制。
– 不受其分支控制的指令不能在分支之后移动,因此其执行由分支控制。
在介绍了各种类型的数据依赖和控制依赖之后,让我们来讨论这些依赖是如何在管道中引起问题的。依赖是程序的属性,并且依赖是否会导致管道中的危险和停顿是管道组织的属性。
根据指令中读写访问的顺序,数据危险可以归为以下三种类型之一:
RAW(写后读)
对应于真正的数据依赖
必须保留程序顺序
这种危险源于实际需要的交流
考虑两条指令 i 和 j,指令 j 在 i 写入数据之前先读取数据
i: 添加 R1、R2、R3
j: SUB R4, R1, R3
Add 修改 R1,然后 Sub 应该读取它。如果更改此顺序,则存在 RAW 危险
WAW(写后写)
对应于输出依赖
当有多个写入或一个较短的整数流水线和一个较长的浮点流水线或当前一条指令停止时一条指令继续执行时发生 WAW(写入后写入)
这是由名称依赖性引起的。没有实际的数据传输。它是导致问题的同名
考虑两条指令 i 和 j,指令 j 应该在指令 i 写入数据之后写入
i: SUB R1, R4, R3
j:添加 R1、R2、R3
指令 i 必须先修改寄存器 r1,然后 j 必须修改它。否则,存在 WAW 危险。由于 R1 存在问题。如果使用了其他寄存器,则不会有问题
解决方案是寄存器重命名,即使用其他寄存器。硬件可以进行重命名或编译器可以进行重命名
WAR(读后写)
产生于反依赖
在大多数静态问题管道中不会发生
发生在有早写和晚读,或指令重新排序时
没有实际的数据传输。它是导致问题的同名
考虑到两条指令 i 和 j,指令 j 应该在指令 i 读取数据之后写入。
i: SUB R4, R1, R3
j:添加 R1、R2、R3
指令 i 必须先读取寄存器 r1,然后 j 必须修改它。否则,存在 WAR 危险。由于 R1 存在问题。如果使用了其他寄存器,则不会有问题
• 解决方案是寄存器重命名,即使用其他寄存器。硬件可以进行重命名或编译器可以进行重命名
图 11.3 给出了具有真实数据依赖性的情况。在接下来的三个指令中使用 ADD 指令的结果会导致危险,因为直到这些指令读取寄存器之后才会写入寄存器。ADD 指令的回写仅发生在第 5 个时钟周期,而接下来的三个指令读取之前的寄存器值,因此会读取错误的数据。这会导致 RAW 危险。
控制风险是当我们需要找到分支的目的地时,并且在我们知道该目的地之前无法获取任何新指令。图 11.4 说明了控制危险。第一条指令是一个分支,它只在第四个时钟周期内得到解决。因此,接下来提取的三个指令可能是正确的,也可能是错误的,这取决于分支的结果。这是控制危害的一个例子。
现在,在讨论了各种相关性和它们可能导致的危害之后,我们将了解在我们的简单 MIPS 管道中可能发生的危害是什么。
结构性危害
资源使用冲突
• 在具有单个内存的 MIPS 流水线中
– 加载/存储需要数据访问
– 取指令必须在那个周期内停止
• 会导致管道“泡沫”
• 因此,流水线数据路径需要单独的指令/数据存储器或单独的指令/数据缓存
RAW 危害——可能发生在任何架构中
WAR 危害——不能在 MIPS 5 阶段流水线中发生,因为所有指令都需要 5 个阶段,读取总是在第 2 阶段,写入总是在第 5 阶段
WAW 危害——不能在 MIPS 5 阶段流水线中发生,因为所有指令都需要 5 个阶段,而写入总是在第 5 阶段
控制危害
可以发生
惩罚取决于何时解决分支——在第二个时钟周期或第三个时钟周期
更积极的实现解决了第二个时钟周期本身的分支,导致一个时钟周期的损失
让我们看一下带有停顿的加速方程并看一个示例问题。
CPIpipelined = 理想 CPI + 每个 Inst 的平均停顿周期
假设我们要比较两台机器的性能。哪个机器更快?
机器 A:双端口内存——因此没有内存停顿
机器 B:单端口内存,但其流水线实现的时钟频率快 1.05 倍
认为:
两者的理想 CPI = 1
加载是执行指令的 40%
SpeedupA = 流水线深度/(1+0) x(clockunpipe/clockpipe)
= 管道深度
SpeedupB = 管道深度/(1 + 0.4 x 1) x (clockunpipe/(clockunpipe / 1.05)
=(管道深度/1.4)x 1.05
= 75 x 管道深度
SpeedupA / SpeedupB = 流水线深度 / (0.75 x 流水线深度) = 1.33 机器 A 的速度提高了 1.33 倍。
总而言之,我们已经讨论了管道中可能发生的各种危险。结构性风险的发生是因为没有足够的资源重复,它们必须在设计时本身进行处理。数据危险的发生是因为真正的数据依赖和名称依赖。控制危害是由分支引起的。所有这些危害的解决方案将在后续模块中讨论。
网页链接/支持材料
计算机组织与设计——硬件/软件接口,David A. Patterson 和 John L. Hennessy,第 4 版,Morgan Kaufmann,Elsevier,2009 年。
计算机组织,Carl Hamacher、Zvonko Vranesic 和 Safwat Zaky,第 5 版,McGraw-Hill 高等教育,2011 年。